#include "CogTest.h"
#include "CogProcessors.hpp"
#include "CogEvolver.h"
#include <boost/tokenizer.hpp>

// ------------------------------------------------------------------------------------------

double CogCacheEvaluator::evaluate(CogProduction* pRule)
{
	if (!pRule)
	{
		return 0;
	}
	if (cacheMode == COGCM_VALUE)
	{
		if (pRule->lhs.flag & COGSF_EVAL)
		{
			if (*(pRule->pCache))
			{
				return 1;
			}
		}
	}
	double effectiveSize = 1;
	for (size_t i = 0; i < pRule->rhs.size(); i++)
	{
		effectiveSize += evaluate(pRule->rhs[i].pRule);
	}
	return effectiveSize;
}

// ------------------------------------------------------------------------------------------

double CogTESizeEvaluator::evaluate(CogProduction* pRule)
{
	double teSize = 0;
	for (size_t i = 0; i < pRule->rhs.size(); i++)
	{
		if (pRule->rhs[i].flag & COGSF_TERMINAL)
		{
			teSize += 1.0;
		}
		else
		{
			teSize += evaluate(pRule->rhs[i].pRule);
		}
	}
	return teSize;
}

// ------------------------------------------------------------------------------------------

double CogNTSizeEvaluator::evaluate(CogProduction* pRule)
{
	return static_cast<double>(pRule->ntSize);
}

// ------------------------------------------------------------------------------------------

double CogAgeEvaluator::evaluate(CogProduction* pRule)
{
	return static_cast<double>(pRule->lhs.pPool->getGrammar()->pEvolver->parameters.maxEpochs - pRule->generation);
}

// ------------------------------------------------------------------------------------------

CogBinomialEvaluator::CogBinomialEvaluator(const string& parameters)
{
	int numSamples = atoi(parameters.c_str());
	if (numSamples <= 0) 
	{
		globalMessageLog.print(COGMT_INFO_PARAMETER, VERBOSITY_NORMAL, "Binomial-3 evaluator parameter <%s> is empty or invalid; assuming a sample size of 21", parameters.c_str());
		numSamples = 21;
	}
	variables.reserve(numSamples);
	targets.reserve(numSamples);
	double input = -1;
	while (input <= 1)
	{
		variables.push_back(vector<double>());
		variables.back().push_back(input);
		targets.push_back((input + 1) * (input + 1) * (input + 1));
		input += 2.0/numSamples;
	}
	maxVariables = 1;
}

// ------------------------------------------------------------------------------------------

CogQuarticEvaluator::CogQuarticEvaluator(const string& parameters)
{
	int numSamples = atoi(parameters.c_str());
	if (numSamples <= 0) 
	{
		globalMessageLog.print(COGMT_INFO_PARAMETER, VERBOSITY_NORMAL, "Quartic evaluator parameter <%s> is empty or invalid; assuming a sample size of 20", parameters.c_str());
		numSamples = 20;
	}
	variables.reserve(numSamples);
	targets.reserve(numSamples);
	double input = -1;
	while (input < 1)
	{
		variables.push_back(vector<double>());
		variables.back().push_back(input);
		targets.push_back(input*input*input*input+input*input*input+input*input+input);
		input += 2.0/numSamples;
	}
	maxVariables = 1;
}

// ------------------------------------------------------------------------------------------

CogQuinticEvaluator::CogQuinticEvaluator(const string& parameters)
{
	int numSamples = atoi(parameters.c_str());
	if (numSamples <= 0) 
	{
		globalMessageLog.print(COGMT_INFO_PARAMETER, VERBOSITY_NORMAL, "Quintic evaluator parameter <%s> is empty or invalid; assuming a sample size of 20", parameters.c_str());
		numSamples = 20;
	}
	variables.reserve(numSamples);
	targets.reserve(numSamples);
	double input = -1;
	while (input < 1)
	{
		variables.push_back(vector<double>());
		variables.back().push_back(input);
		targets.push_back(input*input*input*input*input+input*input*input*input+input*input*input+input*input+input);
		input += 2.0/numSamples;
	}
	maxVariables = 1;
}

// ------------------------------------------------------------------------------------------

Cog6thOrderEvaluator::Cog6thOrderEvaluator(const string& parameters)
{
	int numSamples = atoi(parameters.c_str());
	if (numSamples <= 0) 
	{
		globalMessageLog.print(COGMT_INFO_PARAMETER, VERBOSITY_NORMAL, "6th Order Polynomial evaluator parameter <%s> is empty or invalid; assuming a sample size of 20", parameters.c_str());
		numSamples = 20;
	}
	variables.reserve(numSamples);
	targets.reserve(numSamples);
	double input = -1;
	while (input < 1)
	{
		variables.push_back(vector<double>());
		variables.back().push_back(input);
		targets.push_back(input*input*input*input*input*input-2*input*input*input*input-input*input);
		input += 2.0/numSamples;
	}
	maxVariables = 1;
}

// ------------------------------------------------------------------------------------------

CogMultiplexerEvaluator::CogMultiplexerEvaluator(const string& parameters)
{
	int numBits = atoi(parameters.c_str());
	if (numBits <= 0) 
	{
		globalMessageLog.print(COGMT_INFO_PARAMETER, VERBOSITY_NORMAL, "Multiplexer evaluator parameter <%s> is empty or invalid; assuming a 6-bit input", parameters.c_str());
		numBits = 6;
	}
	size_t bitCombis = 1 << numBits;
	variables.reserve(bitCombis);
	targets.reserve(bitCombis);
	int k = 1;
	while (k < numBits)
	{
		k = k << 1;
	}
	k = k >> 1;
	for (size_t i = 0; i < bitCombis; i++)
	{
		variables.push_back(vector<bool>());
		for (size_t b = 1; b < bitCombis; b = b << 1)
		{
			if (i & b)
			{
				variables.back().push_back(1);
			}
			else
			{
				variables.back().push_back(0);
			}
		}
		targets.push_back(variables.back().at(i >> k));
	}
	maxVariables = numBits;
}

// ------------------------------------------------------------------------------------------

CogParityEvaluator::CogParityEvaluator(const string& parameters)
{
	
	boost::char_separator<char> sep(",;", "");
    boost::tokenizer<boost::char_separator<char> > tokens(parameters, sep);
	boost::tokenizer<boost::char_separator<char> >::iterator tok_iter = tokens.begin();
	bool isOdd = false;
	size_t bits = 0;
	if (tok_iter != tokens.end())
	{
		size_t bits = atoi(tok_iter->c_str());
	}
	if (bits <= 0)
	{
		globalMessageLog.print(COGMT_INFO_PARAMETER, VERBOSITY_NORMAL, "Parity evaluator: first parameter in <%s> is empty or invalid; assuming a 5-bit parity problem", parameters.c_str());
		bits = 5;
	}
	if ((tok_iter != tokens.end()) && (++tok_iter != tokens.end()))
	{
		string ltoken = toLower(tok_iter->c_str());
		if (ltoken == "true")
		{
			isOdd = true;
		}
		else if (ltoken != "false")
		{
			globalMessageLog.print(COGMT_INFO_PARAMETER, VERBOSITY_NORMAL, "Parity evaluator: second parameter in <%s> is empty or invalid; assuming an even parity problem", parameters.c_str());
		}
	}
	else
	{
			globalMessageLog.print(COGMT_INFO_PARAMETER, VERBOSITY_NORMAL, "Parity evaluator: second parameter in <%s> is missing; assuming an even parity problem", parameters.c_str());
	}

	size_t bits2 = 1 << bits;
	variables.reserve(bits2);
	targets.reserve(bits2);
	for (size_t i = 0; i < bits2; i++)
	{
		variables.push_back(vector<bool>());
		size_t count = 0;
		for (size_t b = 1; b < bits2; b *= 2)
		{
			if (i & b)
			{
				variables.back().push_back(1);
				count++;
			}
			else
			{
				variables.back().push_back(0);
			}
		}
		targets.push_back(count % 2 == (isOdd ? 1 : 0));
	}
	maxVariables = bits;
}

// ------------------------------------------------------------------------------------------